#include "boxinit.h"

Initer::Initer(QString HD, QString UN,int ctl,ForceOffLine** FOL)
{
    homeDir=HD;
    userName=UN;
    ctl_sock=ctl;
    FL=FOL;
}

//本地路径和服务器路径的转换
void Initer::decode_path(const char *from, char *to)
{
    int i = 0, counter = 0;
    for(; i < strlen(from) ; ++i)
    {
            if(from[i] == '/')
                    counter++;
            if( counter == 3 )
                    break;
    }
    char tmp[PATH_LEN-i];
    sprintf(tmp,from+i);
    QString downloadDir=homeDir+QString(tmp);
    sprintf(to,downloadDir.toStdString().c_str());
}

//为一个文件夹设置徽标
void Initer::setEmblemForDir(QString path)
{
    DIR* pdir;
    struct dirent *dir;
    setIconFinished(path);
    pdir=opendir(path.toStdString().c_str());
    while((dir=readdir(pdir))!=NULL)
    {
        if(dir->d_type&DT_DIR)
        {
            if(strcmp(dir->d_name,".")==0||strcmp(dir->d_name,"..")==0)
                continue;
            QString temp=QString(path)+'/'+QString(dir->d_name);
            setEmblemForDir(temp);
        }
        else
            {

            QString tmp=QString(path)+'/'+QString(dir->d_name);
            cout<<tmp.toStdString()<<endl;
            setIconFinished(tmp);
        }
    }
}


//下载文件
void Initer::pull_file(const char *serverPath, const char *path)
{
    int tsf_sock = tcp_connect(SRV_IP, SRV_DATA_PORT);
    if( tsf_sock < 0 )
        {
            perror("connect");
            return;
        }

    //发送PUSH请求
    uint8_t request = DATA_PULL;
    if( write(tsf_sock,&request,sizeof(uint8_t)) < 0 )
    {
        perror("send request to 1235");
        return;
    }

    //发送用户名
    char usr_name[USR_LEN];
    strncpy(usr_name,userName.toStdString().c_str(),USR_LEN);
    if( writen(tsf_sock,usr_name,USR_LEN) < 0)
    {
        perror("send usr_name to 1235");
        return;
    }

    //发送目录
   // snprintf(log.str.path, PATH_LEN, "%s%s", SRV_ROOT, temp_path + i);

    if(writen(tsf_sock,serverPath,PATH_LEN) < 0 )
    {
        perror("send path to 1235");
        return;
    }

    //char path[PATH_LEN];
    //snprintf(path,PATH_LEN,"/home/%s/gsyncer%s",cuserid(NULL),log.str.path+strlen(SRV_ROOT));
    int fd_temp = ::open(path,O_WRONLY|O_TRUNC|O_CREAT);
    if( fd_temp < 0 )
    {
        cout<<"file "<<path<<" not exists now \n";
        return;

    }
    //Writen(tsf_sock,&head, sizeof(head));

    file_copy(fd_temp, tsf_sock);
    printf("%s download \n",path);
    ::close(fd_temp);
    ::close(tsf_sock);
}

//从我们的initmap中删除一个文件时候，判断他的父文件夹是否需要被删除
void Initer::rmDirFromMap(const char *dirName)
{
    DIR* pdir;
    struct dirent *dir;
    pdir=opendir(dirName);
    while((dir=readdir(pdir))!=NULL)
    {
        if(dir->d_type&DT_DIR)
        {
            if(strcmp(dir->d_name,".")==0||strcmp(dir->d_name,"..")==0)
                continue;
            QString temp=QString(dirName)+'/'+QString(dir->d_name);
            rmDirFromMap(temp.toStdString().c_str());
        }
        else
            {
            QString tmp=QString(dirName)+'/'+QString(dir->d_name);
            if(initFliter.find(tmp)!=initFliter.end())
                initFliter.remove(tmp);
        }
    }
}

//  如果是移动一个文件夹事件，递归的将文件夹中在map（用于记录最后有那些文件要下载）中的文件进行重命名
void Initer::mvDirFromMap(const char *path1, const char *path2)
{
    cout<<"moveDir"<<endl;
    DIR* pdir;
    struct dirent *dir;
    pdir=opendir(path1);
    while((dir=readdir(pdir))!=NULL)
    {
        if(dir->d_type&DT_DIR)
        {
            if(strcmp(dir->d_name,".")==0||strcmp(dir->d_name,"..")==0)
                continue;
            QString temp=QString(path1)+'/'+QString(dir->d_name);
            QString temp2=QString(path2)+'/'+QString(dir->d_name);
            mvDirFromMap(temp.toStdString().c_str(),temp2.toStdString().c_str());
        }
        else
            {
            QString tmp=QString(path1)+'/'+QString(dir->d_name);
            QString tmp2=QString(path2)+'/'+QString(dir->d_name);
            if(initFliter.find(tmp)!=initFliter.end())
            {
                initFliter[tmp2]=this->pathToServerPath(tmp2);
                cout<<tmp.toStdString()<<"在表中被移动为"<<tmp2.toStdString()<<endl;
                initFliter.remove(tmp);
            }
        }
    }
}

//基本的功能函数之一，把一个文件或者文件夹的徽标设置为在更新中
void Initer::setIconUpdate(QString filename)
{
    filename.append("'").prepend("'");
    QString tmp="/usr/share/GeeBox/emblem "+filename+" ubuntuone-updating";
    system(tmp.toStdString().c_str());
}

//基本的功能函数之一，把一个文件或者文件夹的徽标设置为更新完成
void Initer::setIconFinished(QString filename)
{
    filename.append("'").prepend("'");
    QString tmp="/usr/share/GeeBox/emblem "+filename+" ubuntuone-synchronized";
    system(tmp.toStdString().c_str());
}


//递归删除一个文件夹
int Initer::dfs_rmdir(const char *path)
{
    struct stat info;
    if( stat(path,&info) < 0 )
    {
            perror("stat");
            return -1;
    }


    if( S_ISDIR(info.st_mode) )     //如果是目录  则递归删除
    {
            DIR *pre = opendir(path);
            if( pre == NULL  )
            {
                    perror("opendir");
                    return -1;
            }

            struct dirent *entry = readdir(pre);
            while(entry != NULL)
            {
                    if( strcmp(entry->d_name,".") == 0 ||
                                    strcmp(entry->d_name,"..") == 0 )
                    {
                            entry = readdir(pre);
                            continue;
                    }

                    string temp(path);
                    temp += "/";
                    temp += entry->d_name;

                    if( stat(temp.c_str(),&info) )
                    {
                            perror("stat");
                            return -1;
                    }

                    if(S_ISDIR(info.st_mode) )
                    {
                            dfs_rmdir(temp.c_str());
                    }
                    else
                    {
                            unlink(temp.c_str());
                    }

                    entry = readdir(pre);
            }
            closedir(pre);
            rmdir(path);
            return 0;

    }
    else                    //如果是文件  直接删除
    {
            return unlink(path);
    }
}

void Initer::run()
{
    cout<<homeDir.toStdString()<<endl;
    //读取本地版本号
    int local_version;
    QString version_path = QDir::homePath()+"/.GeeBox/"+userName+"/.version";

    QFile localVersionFile(version_path);
    if(localVersionFile.open(QFile::ReadOnly))
    {
        QTextStream out(&localVersionFile);
        out>>local_version;
    }
    localVersionFile.close();
    long tmpSize=this->countSizeForDir(homeDir.toStdString().c_str());
    emit sizeChanged(tmpSize);

    emit this->startInit();

    //读取服务器端的版本号
    int real_version;
    if( readn(ctl_sock,&real_version,sizeof(int)) < 0 )
    {

            ::close(ctl_sock);
            return;
    }

    cout << local_version <<" " << real_version<< endl;

    (*FL)=new ForceOffLine(userName);
    emit ForceOffLineWatchStart();
    (*FL)->start();

    //若本地版本号比服务器小  说明需要同步
    if(local_version < real_version)
    {

            int sync_fd = tcp_connect(SRV_IP,SRV_DATA_PORT);

            //发送请求号
            uint8_t request = DATA_SYNC;
            int res;

            if( (res = writen(sync_fd,&request,sizeof(request))) <0 )
            {
                    perror("send request");
                    ::close(sync_fd);
            }

            //发送用户ID
            char usr_name[USR_LEN];
            strncpy(usr_name,userName.toStdString().c_str(),USR_LEN);
            if((res=writen(sync_fd, usr_name, USR_LEN)) <0 )
            {
                    perror("send id");
                    ::close(sync_fd);
            }

            //发送本地版本号
            if(writen(sync_fd,&local_version,sizeof(int)) <0)
            {
                    cout << local_version << endl;
                    perror("send local");
                    ::close(sync_fd);
            }

            //发送服务器版本号
            if(writen(sync_fd,&real_version,sizeof(int)) < 0)
            {
                    cout << local_version << endl;
                    perror("send real");
                    ::close(sync_fd);
            }

            //接收日志
            ctl_pkg_t logs;
            //char prepath[USR_LEN];
            //snprintf(prepath,USR_LEN,"/home/%s/gsyncer",cuserid(NULL));
            char path[PATH_LEN];

            while(true)
            {
                int res;
                if( (res =readn(sync_fd,&logs,sizeof(logs))) <= 0 )
                {
                    if(res < 0)
                    {
                        perror("read from sync-fd");
                    }
                    ::close(sync_fd);
                    break;
                }
                decode_path(logs.str.path,path);
                //cout<<int(logs.type)<<endl;



                if(logs.type == CREATE_D)
                {
                    printf("dir %s is created\n",path);
                    if(mkdir(path,0766) < 0 )
                        perror("mkdir 1");
                    else
                        this->setIconFinished(QString(path));
                }

                else if ( logs.type == CREATE_F )
                {
                    printf("file %s is created\n",path);
                    int fd = ::open(path,O_RDONLY|O_CREAT,0666);
                    if( fd < 0 )
                        perror("creat");
                    else
                        this->setIconFinished(QString(path));

                    ::close(fd);

                }
                else if ( logs.type == DEL_F )
                {
                    printf("file %s is deleted\n",path);
                    if(initFliter.find(QString(path))!=initFliter.end())
                        initFliter.remove(QString(path));
                    if( unlink(path) < 0 )
                        perror("unlink");
                }
                else if ( logs.type == DEL_D )
                {
                    printf("dir %s is deleted\n",path);
                    this->rmDirFromMap(path);
                    if( dfs_rmdir(path) < 0 )
                        perror("rmdir");
                }
                else if ( logs.type == UP_F )
                {
                    printf("file %s is updated\n",path);
                    initFliter[QString(path)]=QString(logs.str.path);
                }
                else if ( logs.type == UP_D )
                {

                }
                else if(logs.type==MOVE)
                {
                    char path2[PATH_LEN];

                    if( (res =readn(sync_fd,&logs,sizeof(logs))) <= 0 )
                    {
                        if(res < 0)
                        {
                            perror("read from sync-fd");
                        }
                        ::close(sync_fd);
                        break;
                    }
                    decode_path(logs.str.path,path2);
                    if(QDir(QString(tr(path))).exists())
                    {
                        printf("dir %s is move to %s\n",path,path2);
                        this->mvDirFromMap(path,path2);
                    }
                    else if(initFliter.find(QString(path))!=initFliter.end())
                    {
                        initFliter[QString(path2)]=this->pathToServerPath(QString(path2));
                        initFliter.remove(QString(path));
                    }
                    if(rename(path,path2)<0)
                        perror("rename");
                    else
                    {
                        if(QDir(QString(tr(path2))).exists())
                        {
                            cout<<"给移动后的文件夹"<<QString(path2).toStdString()<<endl;
                            this->setEmblemForDir(QString(path2));
                        }
                        else
                            this->setIconFinished(QString(path2));
                    }
                }

            }
    }

    for(QMap<QString,QString>::iterator it=initFliter.begin();it!=initFliter.end();it++)
    {
        this->SearchFile(it.key());
    }
    for(QMap<QString,QString>::iterator it=initFliter.begin();it!=initFliter.end();it++)
    {
        cout<<"将要下载"<<it.key().toStdString()<<endl;
        pull_file(it.value().toStdString().c_str(),it.key().toStdString().c_str());
        this->rmFromIconMap(it.key());
    }

    cout<<endl;

    //更新本地本地版本号
    //发送信号，把服务器的版本号发送给Controller，去更新本地版本号
    emit upVersion(real_version);
    if(localVersionFile.open(QFile::WriteOnly))
    {
        QTextStream in(&localVersionFile);
        in<<real_version<<"\n";
    }
    localVersionFile.close();
    tmpSize=this->countSizeForDir(homeDir.toStdString().c_str());
    emit sizeChanged(tmpSize);
    emit this->initFinished();


//    this->SearchFile("/home/liu/geebox/8/geebox/dir1/dir2/m");
//    for(QMap<QString,int>::iterator it=IconMap.begin();it!=IconMap.end();it++)
//    {
//        cout<<it.key().toStdString()<<" "<<it.value()<<endl;
//    }
//    this->rmFromIconMap("/home/liu/geebox/8/geebox/dir1/dir2/m");
}

//设置一个文件以及他的父目录为更新中
void Initer::SearchFile(QString fileName)
{
    QString tmp=fileName;
    QString tmp2=homeDir;
    tmp2.append("/");
    tmp.remove(tmp2);
    QStringList sList=tmp.split("/");
    tmp2=homeDir;
    for(QStringList::iterator it=sList.begin();it!=sList.end();it++)
    {
        tmp2.append("/");
        tmp2+=(*it);
        if(IconMap.find(tmp2)!=IconMap.end())
        {
            IconMap[tmp2]++;
        }
        else
        {
            IconMap[tmp2]=1;
        }
        this->setIconUpdate(tmp2);
    }
}
//设置一个文件为更新完成，同时判断是否需要将他的父目录设置为更新完成
void Initer::rmFromIconMap(QString fileName)
{
    QString tmp=fileName;
    QString tmp2=homeDir;
    tmp2.append("/");
    tmp.remove(tmp2);
    QStringList sList=tmp.split("/");
    tmp2=homeDir;
    for(QStringList::iterator it=sList.begin();it!=sList.end();it++)
    {
        tmp2.append("/");
        tmp2+=(*it);
        if(IconMap.find(tmp2)!=IconMap.end())
        {
            if((--IconMap[tmp2])==0)
            {
                IconMap.remove(tmp2);
                this->setIconFinished(tmp2);
            }
        }
    }
}


long Initer::countSizeForDir(const char *name)
{
        long size=0;
        struct dirent *dir;
        DIR* pdir;
                    struct stat buf;
                    stat(name,&buf);
                    size+=(int)buf.st_size;
        pdir=opendir(name);
        while((dir=readdir(pdir))!=NULL)
        {
            if(dir->d_type&DT_DIR)
            {
                if(strcmp(dir->d_name,".")==0||strcmp(dir->d_name,"..")==0)
                    continue;
                else
                {
                    char temp[256];
                    strcpy(temp,name);
                    strcat(temp,"/");
                    strcat(temp,dir->d_name);
                    size+=countSizeForDir(temp);
                }
            }
            else
            {
                    char temp[256];
                    strcpy(temp,name);
                    strcat(temp,"/");
                    strcat(temp,dir->d_name);
                    struct stat buf;
                    stat(temp,&buf);
                    size+=(long)buf.st_size;
            }
        }
        return size;
}

long Initer::countSizeForFile(const char *name)
{
    struct stat buf;
    stat(name,&buf);
    return (long)buf.st_size;
}

QString Initer::pathToServerPath(QString filePath)
{
    QString tmp=filePath;
    tmp.remove(homeDir).prepend(QString(userName.toStdString().c_str())).prepend("/gsync/");
    return tmp;
}
